package ubi3d.Data;

/*
 * Recieves live RTLS data from a Ubisense system
 
   Copyright 2009 BBC Research, David Lewis, <david.lewis4@bbc.co.uk>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */
import Network.LocationMessage;
import Network.Message;
import Network.MessageParser;
import Network.UbiSenseCoords;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Hashtable;
import javax.swing.JOptionPane;

public class LiveThread extends Thread {

    @Override
    public void run() {

        //Create recieving space for packet
        byte[] bytePacket = new byte[52];
        DatagramPacket packet = new DatagramPacket(bytePacket, bytePacket.length);
       // System.out.println("Setting up packet");
        //Loop to keep recieving
        while (!Thread.interrupted()) {

            //Try and recieve packet
            try {
                socket.receive(packet);
               // System.out.println("recieved packet");
                bytePacket = packet.getData();

                //process message
                processMessage(bytePacket);

                //error handling
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(ubi3d.Main.gui, "Socket error", ex.getMessage(), JOptionPane.WARNING_MESSAGE);
                System.out.println("Packet handling fail");
                //java.util.logging.Logger.getLogger(DBLogger.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Indicates if this thread has a socket open
     * @return If the socket is open
     */
    public boolean socketOpen() {
        return socket.isConnected();
    }

    public static void main() {
    }

    /**
     * Constructor
     * @param address   Database address e.g. jdbc:mysql://localhost:3306/events
     * @param parent    DataSource which created the thread (may be used to return results - not implemented)
     * @param port
     */
    public LiveThread(String address, DataSource parent, int port) {
        filter = new Hashtable<Integer, ArrayList<Long[]>>();
        try {
            //Connect socket
            socket = new DatagramSocket(port);

            System.out.println(socket.isBound());

            System.out.println("Live socket open");
            //Error handling
        } catch (SocketException ex) {
            JOptionPane.showMessageDialog(ubi3d.Main.gui, "Socket error", ex.getMessage(), JOptionPane.WARNING_MESSAGE);
            System.out.println("Socket exception");
        }
    }

    /**
     * Extract useful data from the raw packet
     * @param bytePacket Bytes from raw Ubisense packet
     */
    private void processMessage(byte[] bytePacket) {
         //Parse message
        Message receivedMessage = new MessageParser().parseMessage(bytePacket);

        //If this is a location message type with coordinates
        if (receivedMessage.getMessageType() == locationMessageHex) {

            LocationMessage message = (LocationMessage) receivedMessage;
            UbiSenseCoords messageCoords = message.getCoords();

            //Extract coordinates and important info
            Double x = messageCoords.getX();
            Double y = messageCoords.getY();
            Double z = messageCoords.getZ();

            float error = message.getError();

            double time = messageCoords.getH() * 3600000 + messageCoords.getM() * 60000 +
                    messageCoords.getS() * 1000 + messageCoords.getMs();

            long tag = message.getTag_id_bottom();

            
            //If filtering should be applied
            if (ubi3d.Main.gui.filtered) {
                //Array current position
                Long[] position = {Math.round(x), Math.round(y), Math.round(z)};

                //get previous positions for this tag or clear a space
                ArrayList<Long[]> previousPositions = filter.get(Math.round(tag));
                filter.remove(Math.round(tag));
                if (previousPositions == null) {
                    previousPositions = new ArrayList<Long[]>();
                }

                //check we have enough previous to start filtering
                if (previousPositions.size() >= windowSize + 1) {
                    previousPositions.remove(0);

                    previousPositions.add(position);

                    //Work out average
                    Long[] average = {0l, 0l, 0l};
                    for (int i = 0; i < previousPositions.size(); i++) {
                        average[0] += previousPositions.get(i)[0];
                        average[1] += previousPositions.get(i)[1];
                        average[2] += previousPositions.get(i)[2];
                    }

                    average[0] = average[0] / previousPositions.size();
                    average[1] = average[1] / previousPositions.size();
                    average[2] = average[2] / previousPositions.size();

                    //Send appropriately rounded results
                    ubi3d.Main.gui.updatePosition((int)tag, Math.round(average[0]), Math.round(average[1]), Math.round(average[2]), Math.round(Math.round(time)));

                } else {
                    previousPositions.add(position);

                }
                filter.put(Math.round(tag), previousPositions);
            } else {
                //Send appropriately rounded results
                System.out.println("Tag: "+tag+" coords: "+x+" "+y+" "+z+ "b");
                ubi3d.Main.gui.updatePosition((int)tag, x.floatValue(), y.floatValue(), z.floatValue(), Math.round(Math.round(time)));
            }
        }
    }
    Hashtable<Integer, ArrayList<Long[]>> filter;
    private DatagramSocket socket;
    private char locationMessageHex = 0x26a;
    private int windowSize = 10;
}
